using System.Collections.Generic;
using NyMPH.Persistence;
using NyMPH.Patches;
using System;
using System.Data.Linq.Mapping;

[Table(Name = "tbKeyword")]
public class Keyword : IPersistence, IEquatable<Keyword>, IComparable<Keyword>
{
    private int _OID;
    private string _name;
    private bool _isCustom;
    private SDKCategories _type;
    private bool _trackChanges = true;
    private bool _isDeleted = false;
    private bool _isModified = false;

    #region Properties

    /// <summary>
    /// The unique ObjectID within the NyMPH Patch table.
    /// </summary>
    [Column(IsPrimaryKey = true, Name = "KeywordOID", Storage = "_OID")]
    public int OID
    {
        set
        {
            _OID = value;
           SetModified();
        }
        get { return _OID; }
    }

    /// <summary>
    /// The Name of the Sound Description Keyword
    /// </summary>
    [Column(Name = "KeywordName", Storage = "_name")]
    public string Name
    {
        set
        {
            _name = value;
           SetModified();
        }
        get { return _name; }
    }

    /// <summary>
    /// True if this Sound Description Keyword is custom-made (i.e. not part of the original set), otherwise false.
    /// </summary>
    [Column(Name = "IsCustom", Storage = "_isCustom")]
    public bool IsCustom
    {
        set
        {
            _isCustom = value;
           SetModified();
        }
        get { return _isCustom; }
    }

    /// <summary>
    /// The Category of this Sound Description Keyword (SoundSource, InstrumentType, Timbre_Characteristcs, or Articulation
    /// </summary>
    [Column(Name = "KeywordType", Storage = "_type", DbType = "string")]
    public SDKCategories KeywordType
    {
        set
        {
            _type = value;
           SetModified();
        }
        get { return _type; }
    }

    /// <summary>
    /// Indicates the persistence state of this object (New, Deleted, Modified, or Unchanged)
    /// </summary>
    public  PersistenceStates PersistenceState
    {
        get
        {
            if (_OID == -1)
            {
                //Without an OID it doesn't exist in the database
                return PersistenceStates.New;
            }

            if (_isDeleted)
            {
                //Being Deleted takes precedence over being Modified or Unchanged, 
                return PersistenceStates.Deleted;
            }

            if (_isModified)
            {
                //A value has changed since this instance was created AND it has a valid OID (because the first check for OID exited the accessor
                return PersistenceStates.Modified;
            }
            else
            {
                //This instance has an OID, is not Deleted, and nothing has changed
                return PersistenceStates.Unchanged;
            }
        }
    }

    #endregion

    #region Methods
    public static List<Keyword> GetKeywordList(SDKCategories Type)
    {
        // TODO add implementation
        //TODO: call data layer to get list
        throw new NotImplementedException("TODO:");

    }

    /// <summary>
    /// Sets the 'modified' flag if change tracking is enabled.
    /// </summary>
    private void SetModified()
    {
        if (_trackChanges)
        {
            _isModified = true;
        }
    }

    /// <summary>
    /// Allows properties to be set without marking this instance as 'Modified'
    /// </summary>
    public void SuspendChangeTracking()
    {
        _trackChanges = false;
    }

    /// <summary>
    /// Changes to properties will flag this instance as 'Modified'
    /// </summary>
    public void ResumeChangeTracking()
    {
        _trackChanges = true;
    }

    /// <summary>
    /// Returns the Name given to this Keyword instance.
    /// </summary>
    /// <returns>The Name property for this instance.</returns>
    public override string ToString()
    {
        return this.Name;
    }

    #endregion

    #region IPersistence Members

     public bool Save()
    {
        throw new System.NotImplementedException();
    }

    public bool Load()
    {
        throw new System.NotImplementedException();
    }

    public bool Delete()
    {
        throw new System.NotImplementedException();
    }

    public int GetOID()
    {
        throw new System.NotImplementedException();
    }

    public void Find(Criteria item)
    {
        throw new System.NotImplementedException();
    }

    public bool HasChanges()
    {
        throw new System.NotImplementedException();
    }

    public void HasChanges(bool Changed)
    {
        throw new System.NotImplementedException();
    }

    #endregion

    #region IEquatable<Keyword> Members

    /// <summary>
    /// Determines whether this instance of Keyword and a specified object, which must also be a Keyword object, have the same values.
    /// </summary>
    /// <param name="Obj">The Keyword object to compare values with</param>
    /// <returns>True if all properties have matching values, otherwise false.</returns>
    /// <remarks></remarks>
    public bool Equals(Keyword Obj)
    {
        if (object.Equals(Obj, null))
        {
            return object.Equals(this, null);
        }
        else
        {
            //AND all equality evaluations together
            return ((this.Name == Obj.Name) && (this.KeywordType == Obj.KeywordType) && (this.IsCustom == Obj.IsCustom));

        }
    }

    /// <summary>
    /// Determines whether this instance of Keyword and a specified object, which must also be a Keyword object, have the same values.
    /// </summary>
    /// <param name="Obj">The Keyword to compare values with</param>
    /// <returns>True if all properties have matching values, otherwise false.</returns>
    /// <remarks>Overrides the Object.Equals(object) method. This ensures all invocations of Equals() for the Keyword type return the same result.</remarks>
    public override bool Equals(object Obj)
    {
        //use base class method for comparing nulls; casting them would throw an exception
        if (Obj == null) return base.Equals(Obj);

        //make sure object can be cast to Keyword and call the Equals(Keyword) method
        if (Obj is Keyword)
        {
            return Equals((Keyword)Obj);
        }
        else
        {
            throw new InvalidCastException("The 'Obj' argument is not a Keyword object.");
        }
    }

    /// Provides a hash value based on XORing the Name, KeywordType, and IsCustom properties together along with the number of Patches it holds. Two Keyword objects with the same values will produce the same hash code, although it isn't guaranteed to be unique
    /// </summary> 
    /// <returns>The hash created from (Name xor KeywordType xor IsCustom)</returns>    
    /// <remarks>MSDN recommends overriding this method when implementing IEquatable. </remarks>
    public override int GetHashCode()
    {
        return this.Name.GetHashCode() ^ this.KeywordType.GetHashCode();
    }

    /// <summary>
    /// Create equality operator for Keyword objects; allows expressions like {if (KeywordA = KeywordB)} to compare each member value instead of the object reference
    /// </summary>
    public static bool operator ==(Keyword KeywordA, Keyword KeywordB)
    {
        return KeywordA.Equals(KeywordB);
    }

    /// <summary>
    /// Create inequality operator for Keyword objects; allows expressions like {if (KeywordA != KeywordB)} to compare each member value instead of the object reference
    /// </summary>
    public static bool operator !=(Keyword KeywordA, Keyword KeywordB)
    {
        return !KeywordA.Equals(KeywordB);
    }

    #endregion

    #region IComparable<Keyword> Members

    /// <summary>
    /// This is required to implement IComparable, which is used when sorting Keywords. These are sorted by Name only.
    /// </summary>
    /// <param name="other">The Keyword object to compare values with</param>
    /// <returns>Less than 0 if 'other' Keyword should be earlier in the sort order, 0 if equal, and greater than zero if the 'other' Keyword should come after this Core.</returns>
    /// <remarks></remarks>
    public int CompareTo(Keyword other)
    {
        //invoke Compare() method of string
        return string.Compare(this.Name + this.KeywordType.ToString(), other.Name + other.KeywordType.ToString());
    }

    #endregion
}
